/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
 
This file is part of Quake III Arena source code.
 
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
 
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// win_syscon.h
#include "../client.h"
#include "win_local.h"
#include "resource.h"
#include <errno.h>
#include <float.h>
#include <fcntl.h>
#include <stdio.h>
#include <direct.h>
#include <io.h>
#include <conio.h>

#define COPY_ID			1
#define QUIT_ID			2
#define CLEAR_ID		3

#define ERRORBOX_ID		10
#define ERRORTEXT_ID	11

#define EDIT_ID			100
#define INPUT_ID		101

typedef struct
{
    HWND		hWnd;
    HWND		hwndBuffer;

    HWND		hwndButtonClear;
    HWND		hwndButtonCopy;
    HWND		hwndButtonQuit;

    HWND		hwndErrorBox;
    HWND		hwndErrorText;

    HBITMAP		hbmLogo;
    HBITMAP		hbmClearBitmap;

    HBRUSH		hbrEditBackground;
    HBRUSH		hbrErrorBackground;

    HFONT		hfBufferFont;
    HFONT		hfButtonFont;

    HWND		hwndInputLine;

    char		errorString[80];

    char		consoleText[512], returnedText[512];
    int			visLevel;
    qboolean	quitOnClose;
    int			windowWidth, windowHeight;

    WNDPROC		SysInputLineWndProc;

}
WinConData;

static WinConData s_wcd;

static LONG WINAPI ConWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    char *cmdString;
    static qboolean s_timePolarity;

    switch (uMsg)
    {
    case WM_ACTIVATE:
        if ( LOWORD( wParam ) != WA_INACTIVE )
        {
            SetFocus( s_wcd.hwndInputLine );
        }

        if ( com_viewlog && ( com_dedicated && !com_dedicated->integer ) )
        {
            // if the viewlog is open, check to see if it's being minimized
            if ( com_viewlog->integer == 1 )
            {
                if ( HIWORD( wParam ) )		// minimized flag
                {
                    Cvar_Set( "viewlog", "2" );
                }
            }
            else if ( com_viewlog->integer == 2 )
            {
                if ( !HIWORD( wParam ) )		// minimized flag
                {
                    Cvar_Set( "viewlog", "1" );
                }
            }
        }
        break;

    case WM_CLOSE:
        if ( ( com_dedicated && com_dedicated->integer ) )
        {
            cmdString = CopyString( "quit" );
            Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString );
        }
        else if ( s_wcd.quitOnClose )
        {
            PostQuitMessage( 0 );
        }
        else
        {
            Sys_ShowConsole( 0, qfalse );
            Cvar_Set( "viewlog", "0" );
        }
        return 0;
    case WM_CTLCOLORSTATIC:
        if ( ( HWND ) lParam == s_wcd.hwndBuffer )
        {
            SetBkColor( ( HDC ) wParam, RGB( 0x00, 0x00, 0xB0 ) );
            SetTextColor( ( HDC ) wParam, RGB( 0xff, 0xff, 0x00 ) );

#if 0	// this draws a background in the edit box, but there are issues with this
            if ( ( hdcScaled = CreateCompatibleDC( ( HDC ) wParam ) ) != 0 )
            {
                if ( SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo ) )
                {
                    StretchBlt( ( HDC ) wParam, 0, 0, 512, 384,
                                hdcScaled, 0, 0, 512, 384,
                                SRCCOPY );
                }
                DeleteDC( hdcScaled );
            }
#endif
            return ( long ) s_wcd.hbrEditBackground;
        }
        else if ( ( HWND ) lParam == s_wcd.hwndErrorBox )
        {
            if ( s_timePolarity & 1 )
            {
                SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) );
                SetTextColor( ( HDC ) wParam, RGB( 0xff, 0x0, 0x00 ) );
            }
            else
            {
                SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) );
                SetTextColor( ( HDC ) wParam, RGB( 0x00, 0x0, 0x00 ) );
            }
            return ( long ) s_wcd.hbrErrorBackground;
        }
        break;

    case WM_COMMAND:
        if ( wParam == COPY_ID )
        {
            SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
            SendMessage( s_wcd.hwndBuffer, WM_COPY, 0, 0 );
        }
        else if ( wParam == QUIT_ID )
        {
            if ( s_wcd.quitOnClose )
            {
                PostQuitMessage( 0 );
            }
            else
            {
                cmdString = CopyString( "quit" );
                Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString );
            }
        }
        else if ( wParam == CLEAR_ID )
        {
            SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
            SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, ( LPARAM ) "" );
            UpdateWindow( s_wcd.hwndBuffer );
        }
        break;
    case WM_CREATE:
//		s_wcd.hbmLogo = LoadBitmap( g_wv.hInstance, MAKEINTRESOURCE( IDB_BITMAP1 ) );
//		s_wcd.hbmClearBitmap = LoadBitmap( g_wv.hInstance, MAKEINTRESOURCE( IDB_BITMAP2 ) );
        s_wcd.hbrEditBackground = CreateSolidBrush( RGB( 0x00, 0x00, 0xB0 ) );
        s_wcd.hbrErrorBackground = CreateSolidBrush( RGB( 0x80, 0x80, 0x80 ) );
        SetTimer( hWnd, 1, 1000, NULL );
        break;
    case WM_ERASEBKGND:
#if 0
        HDC hdcScaled;
        HGDIOBJ oldObject;

#if 1	// a single, large image
        hdcScaled = CreateCompatibleDC( ( HDC ) wParam );
        assert( hdcScaled != 0 );

        if ( hdcScaled )
        {
            oldObject = SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo );
            assert( oldObject != 0 );
            if ( oldObject )
            {
                StretchBlt( ( HDC ) wParam, 0, 0, s_wcd.windowWidth, s_wcd.windowHeight,
                            hdcScaled, 0, 0, 512, 384,
                            SRCCOPY );
            }
            DeleteDC( hdcScaled );
            hdcScaled = 0;
        }
#else	// a repeating brush
        {
            HBRUSH hbrClearBrush;
            RECT r;

            GetWindowRect( hWnd, &r );

            r.bottom = r.bottom - r.top + 1;
            r.right = r.right - r.left + 1;
            r.top = 0;
            r.left = 0;

            hbrClearBrush = CreatePatternBrush( s_wcd.hbmClearBitmap );

            assert( hbrClearBrush != 0 );

            if ( hbrClearBrush )
            {
                FillRect( ( HDC ) wParam, &r, hbrClearBrush );
                DeleteObject( hbrClearBrush );
            }
        }
#endif
        return 1;
#endif
        return DefWindowProc( hWnd, uMsg, wParam, lParam );
    case WM_TIMER:
        if ( wParam == 1 )
        {
            s_timePolarity = !s_timePolarity;
            if ( s_wcd.hwndErrorBox )
            {
                InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE );
            }
        }
        break;
    }

    return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    char inputBuffer[1024];

    switch ( uMsg )
    {
    case WM_KILLFOCUS:
        if ( ( HWND ) wParam == s_wcd.hWnd ||
                ( HWND ) wParam == s_wcd.hwndErrorBox )
        {
            SetFocus( hWnd );
            return 0;
        }
        break;

    case WM_CHAR:
        if ( wParam == 13 )
        {
            GetWindowText( s_wcd.hwndInputLine, inputBuffer, sizeof( inputBuffer ) );
            strncat( s_wcd.consoleText, inputBuffer, sizeof( s_wcd.consoleText ) - strlen( s_wcd.consoleText ) - 5 );
            strcat( s_wcd.consoleText, "\n" );
            SetWindowText( s_wcd.hwndInputLine, "" );

            Sys_Print( va( "]%s\n", inputBuffer ) );

            return 0;
        }
    }

    return CallWindowProc( s_wcd.SysInputLineWndProc, hWnd, uMsg, wParam, lParam );
}

/*
** Sys_CreateConsole
*/
void Sys_CreateConsole( void )
{
    HDC hDC;
    WNDCLASS wc;
    RECT rect;
    const char *DEDCLASS = "Q3 WinConsole";
    int nHeight;
    int swidth, sheight;
    int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX;

    memset( &wc, 0, sizeof( wc ) );

    wc.style         = 0;
    wc.lpfnWndProc   = (WNDPROC) ConWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = g_wv.hInstance;
    wc.hIcon         = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1));
    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
    wc.hbrBackground = (void *)COLOR_WINDOW;
    wc.lpszMenuName  = 0;
    wc.lpszClassName = DEDCLASS;

    if ( !RegisterClass (&wc) )
        return;

    rect.left = 0;
    rect.right = 540;
    rect.top = 0;
    rect.bottom = 450;
    AdjustWindowRect( &rect, DEDSTYLE, FALSE );

    hDC = GetDC( GetDesktopWindow() );
    swidth = GetDeviceCaps( hDC, HORZRES );
    sheight = GetDeviceCaps( hDC, VERTRES );
    ReleaseDC( GetDesktopWindow(), hDC );

    s_wcd.windowWidth = rect.right - rect.left + 1;
    s_wcd.windowHeight = rect.bottom - rect.top + 1;

    s_wcd.hWnd = CreateWindowEx( 0,
                                 DEDCLASS,
                                 "Quake 3 Console",
                                 DEDSTYLE,
                                 ( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1,
                                 NULL,
                                 NULL,
                                 g_wv.hInstance,
                                 NULL );

    if ( s_wcd.hWnd == NULL )
    {
        return;
    }

    //
    // create fonts
    //
    hDC = GetDC( s_wcd.hWnd );
    nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY), 72);

    s_wcd.hfBufferFont = CreateFont( nHeight,
                                     0,
                                     0,
                                     0,
                                     FW_LIGHT,
                                     0,
                                     0,
                                     0,
                                     DEFAULT_CHARSET,
                                     OUT_DEFAULT_PRECIS,
                                     CLIP_DEFAULT_PRECIS,
                                     DEFAULT_QUALITY,
                                     FF_MODERN | FIXED_PITCH,
                                     "Courier New" );

    ReleaseDC( s_wcd.hWnd, hDC );

    //
    // create the input line
    //
    s_wcd.hwndInputLine = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER |
                                        ES_LEFT | ES_AUTOHSCROLL,
                                        6, 400, 528, 20,
                                        s_wcd.hWnd,
                                        ( HMENU ) INPUT_ID,	// child window ID
                                        g_wv.hInstance, NULL );

    //
    // create the buttons
    //
    s_wcd.hwndButtonCopy = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
                                         5, 425, 72, 24,
                                         s_wcd.hWnd,
                                         ( HMENU ) COPY_ID,	// child window ID
                                         g_wv.hInstance, NULL );
    SendMessage( s_wcd.hwndButtonCopy, WM_SETTEXT, 0, ( LPARAM ) "copy" );

    s_wcd.hwndButtonClear = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
                                          82, 425, 72, 24,
                                          s_wcd.hWnd,
                                          ( HMENU ) CLEAR_ID,	// child window ID
                                          g_wv.hInstance, NULL );
    SendMessage( s_wcd.hwndButtonClear, WM_SETTEXT, 0, ( LPARAM ) "clear" );

    s_wcd.hwndButtonQuit = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
                                         462, 425, 72, 24,
                                         s_wcd.hWnd,
                                         ( HMENU ) QUIT_ID,	// child window ID
                                         g_wv.hInstance, NULL );
    SendMessage( s_wcd.hwndButtonQuit, WM_SETTEXT, 0, ( LPARAM ) "quit" );


    //
    // create the scrollbuffer
    //
    s_wcd.hwndBuffer = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER |
                                     ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,
                                     6, 40, 526, 354,
                                     s_wcd.hWnd,
                                     ( HMENU ) EDIT_ID,	// child window ID
                                     g_wv.hInstance, NULL );
    SendMessage( s_wcd.hwndBuffer, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );

    s_wcd.SysInputLineWndProc = ( WNDPROC ) SetWindowLong( s_wcd.hwndInputLine, GWL_WNDPROC, ( long ) InputLineWndProc );
    SendMessage( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );

    ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT);
    UpdateWindow( s_wcd.hWnd );
    SetForegroundWindow( s_wcd.hWnd );
    SetFocus( s_wcd.hwndInputLine );

    s_wcd.visLevel = 1;
}

/*
** Sys_DestroyConsole
*/
void Sys_DestroyConsole( void )
{
    if ( s_wcd.hWnd )
    {
        ShowWindow( s_wcd.hWnd, SW_HIDE );
        CloseWindow( s_wcd.hWnd );
        DestroyWindow( s_wcd.hWnd );
        s_wcd.hWnd = 0;
    }
}

/*
** Sys_ShowConsole
*/
void Sys_ShowConsole( int visLevel, qboolean quitOnClose )
{
    s_wcd.quitOnClose = quitOnClose;

    if ( visLevel == s_wcd.visLevel )
    {
        return;
    }

    s_wcd.visLevel = visLevel;

    if ( !s_wcd.hWnd )
        return;

    switch ( visLevel )
    {
    case 0:
        ShowWindow( s_wcd.hWnd, SW_HIDE );
        break;
    case 1:
        ShowWindow( s_wcd.hWnd, SW_SHOWNORMAL );
        SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
        break;
    case 2:
        ShowWindow( s_wcd.hWnd, SW_MINIMIZE );
        break;
    default:
        Sys_Error( "Invalid visLevel %d sent to Sys_ShowConsole\n", visLevel );
        break;
    }
}

/*
** Sys_ConsoleInput
*/
char *Sys_ConsoleInput( void )
{
    if ( s_wcd.consoleText[0] == 0 )
    {
        return NULL;
    }

    strcpy( s_wcd.returnedText, s_wcd.consoleText );
    s_wcd.consoleText[0] = 0;

    return s_wcd.returnedText;
}

/*
** Conbuf_AppendText
*/
void Conbuf_AppendText( const char *pMsg )
{
#define CONSOLE_BUFFER_SIZE		16384

    char buffer[CONSOLE_BUFFER_SIZE*2];
    char *b = buffer;
    const char *msg;
    int bufLen;
    int i = 0;
    static unsigned long s_totalChars;

    //
    // if the message is REALLY long, use just the last portion of it
    //
    if ( strlen( pMsg ) > CONSOLE_BUFFER_SIZE - 1 )
    {
        msg = pMsg + strlen( pMsg ) - CONSOLE_BUFFER_SIZE + 1;
    }
    else
    {
        msg = pMsg;
    }

    //
    // copy into an intermediate buffer
    //
    while ( msg[i] && ( ( b - buffer ) < sizeof( buffer ) - 1 ) )
    {
        if ( msg[i] == '\n' && msg[i+1] == '\r' )
        {
            b[0] = '\r';
            b[1] = '\n';
            b += 2;
            i++;
        }
        else if ( msg[i] == '\r' )
        {
            b[0] = '\r';
            b[1] = '\n';
            b += 2;
        }
        else if ( msg[i] == '\n' )
        {
            b[0] = '\r';
            b[1] = '\n';
            b += 2;
        }
        else if ( Q_IsColorString( &msg[i] ) )
        {
            i++;
        }
        else
        {
            *b= msg[i];
            b++;
        }
        i++;
    }
    *b = 0;
    bufLen = b - buffer;

    s_totalChars += bufLen;

    //
    // replace selection instead of appending if we're overflowing
    //
    if ( s_totalChars > 0x7fff )
    {
        SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
        s_totalChars = bufLen;
    }

    //
    // put this text into the windows console
    //
    SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
    SendMessage( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 );
    SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) buffer );
}

/*
** Sys_SetErrorText
*/
void Sys_SetErrorText( const char *buf )
{
    Q_strncpyz( s_wcd.errorString, buf, sizeof( s_wcd.errorString ) );

    if ( !s_wcd.hwndErrorBox )
    {
        s_wcd.hwndErrorBox = CreateWindow( "static", NULL, WS_CHILD | WS_VISIBLE | SS_SUNKEN,
                                           6, 5, 526, 30,
                                           s_wcd.hWnd,
                                           ( HMENU ) ERRORBOX_ID,	// child window ID
                                           g_wv.hInstance, NULL );
        SendMessage( s_wcd.hwndErrorBox, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
        SetWindowText( s_wcd.hwndErrorBox, s_wcd.errorString );

        DestroyWindow( s_wcd.hwndInputLine );
        s_wcd.hwndInputLine = NULL;
    }
}
